﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameBoardViewer.Common;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameEngine;
using Microsoft.Xna.Framework;
using GameBoardViewer.Animation;

namespace GameBoardViewer.Board2D
{
    public class Board2DControl: IBoard2D, IBoardControl
    {
        protected IMainBoard _mainBoard;
        protected Game _game;
        protected SpriteBatch spriteBatch;
        protected IBoardControl _captureControl;
        protected List<IBoardControl> childControls;
        internal RotationBoardAnimation _rotationAnimation;

        protected GameManager m_GameManager;

        protected Texture2D _txtMainBoard;
        protected Texture2D _txtMainBoardRotated;
        protected Texture2D _txtWhiteMan;
        protected Texture2D _txtBlackMan;
        protected Texture2D _txtWhiteKing;
        protected Texture2D _txtBlackKing;
        protected RenderTarget2D _renderTarget;
        protected Texture2D _txtHighlightField;
        protected Texture2D _txtDropFieldTexture;

        protected GameBoardButton _btnRotateBoard;
        protected float _rotationAngle = 0;

        
        protected Rectangle _lastImageStretch;
        protected int _imageTopMargin = 10 + 50 + 10;

        #region Private Board Metrics

        private const float m_startX = 30;
        private const float m_startY = 30;
        private const float m_endX = 570;
        private const float m_endY = 570;
        private const float m_numFields = 8;
        private const float m_fieldSize = (m_endX - m_startX) / m_numFields;

        #endregion

        AnimationParams _animationParams;
        protected List<Position> _listHighlightFields;
        protected Position _posDragFigure;
        protected Point _pointDragPoint;
        protected Position _posDropField;

        protected ButtonState _lastLeftButtonState;
        protected ButtonState _lastRightButtonState;

        protected Move _currentMove;

        public event EventHandler RenderBackgroundEvent;

        public bool RotatedBlackDown
        { get; set; }

        public bool InEditMode
        {
            get
            {
                return _mainBoard.InEditMode;
            }
        }


        public int GameBoardWidth
        {
            get { return _mainBoard.GameBoardWidth; }
        }

        public int GameBoardHeight
        {
            get { return _mainBoard.GameBoardHeight; }
        }


        public event GameMoveDelegate GameMoveDoneEvent;

        public Board2DControl(Game game, IMainBoard mainBoard)
        {
            _game = game;
            _mainBoard = mainBoard;
            RotatedBlackDown = false;
            _lastImageStretch = Rectangle.Empty;
            _animationParams = new AnimationParams();
            _listHighlightFields = new List<Position>();
            _lastLeftButtonState = ButtonState.Released;
            _lastRightButtonState = ButtonState.Released;
            _pointDragPoint = Point.Zero;
            _posDragFigure = Position.Empty;
            _posDropField = Position.Empty;
            _captureControl = null;
        }

        public void Initialize()
        {
            childControls = new List<IBoardControl>();

            _btnRotateBoard = new GameBoardButton(_game, this);
            _btnRotateBoard.ButtonPressed += new EventHandler(_btnRotateBoard_ButtonPressed);
            childControls.Add(_btnRotateBoard);
        }

        public void LoadContent()
        {
            spriteBatch = new SpriteBatch(_game.GraphicsDevice);

            _txtMainBoard = _game.Content.Load<Texture2D>("sachovnice2D");
            _txtMainBoardRotated = _game.Content.Load<Texture2D>("sachovniceRotated");
            _txtWhiteMan = _game.Content.Load<Texture2D>("kamen_bily");
            _txtBlackMan = _game.Content.Load<Texture2D>("kamen_cerny");
            _txtWhiteKing = _game.Content.Load<Texture2D>("dama_bila");
            _txtBlackKing = _game.Content.Load<Texture2D>("dama_cerna");

            _renderTarget = new RenderTarget2D(_game.GraphicsDevice, _txtMainBoard.Width, _txtMainBoard.Height);

            _txtHighlightField = new Texture2D(_game.GraphicsDevice, 1, 1);
            Color[] data = new Color[1];
            _txtHighlightField.GetData<Color>(data);
            Color clr = Color.LightGoldenrodYellow;
            clr.A = 192;
            data[0] = clr;
            _txtHighlightField.SetData<Color>(data);

            _txtDropFieldTexture = new Texture2D(_game.GraphicsDevice, 1, 1);
            data = new Color[1];
            _txtDropFieldTexture.GetData<Color>(data);
            data[0] = new Color(0, 0, 255, 128);
            _txtDropFieldTexture.SetData<Color>(data);

            _btnRotateBoard.LoadContent("Rotation", 10, 10, enumButtonDockStyle.TopRight, new Color(0, 0, 255, 128),
                        new Color(255, 255, 0, 128));

        }

        public bool Update(MouseState mouse)
        {
            // update board rotation
            if (_rotationAnimation != null)
            {
                float dummy;
                _rotationAnimation.GetRotationParameters(out _rotationAngle, out dummy);
                if(_rotationAnimation.Finished == true)                
                {
                    _rotationAnimation = null;
                    _rotationAngle = 0;
                    RotatedBlackDown = !RotatedBlackDown;
                    return true;
                }              
            }


            return false;
        }

        public void Draw()
        {
            _game.GraphicsDevice.SetRenderTarget(_renderTarget);

            spriteBatch.Begin();

            // draw main board
            if(RotatedBlackDown == false)
                spriteBatch.Draw(_txtMainBoard, new Rectangle(0, 0, _txtMainBoard.Width, _txtMainBoard.Height), Color.White);          
            else
                spriteBatch.Draw(_txtMainBoardRotated, new Rectangle(0, 0, _txtMainBoardRotated.Width, _txtMainBoardRotated.Height), Color.White);          

            // draw figures
            RenderFigures();
            RenderAnimatedFigures();

            if (_posDragFigure == Position.Empty)
            {
                RenderHighlightFields();
            }
            else
            {
                RenderDropField();
            }

            RenderDraggedFigure();

            spriteBatch.End();

            // Print Screen
            _game.GraphicsDevice.SetRenderTarget(null);
            _game.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Color(236, 236, 236), 100.0f, 0);

            if (RenderBackgroundEvent != null)
            {
                RenderBackgroundEvent(this, EventArgs.Empty);
            }

            CalculateImageStretch();

            Vector2 origin = new Vector2(_renderTarget.Width / 2, _renderTarget.Height / 2);
            Vector2 position = new Vector2(_lastImageStretch.X + _lastImageStretch.Width / 2, _lastImageStretch.Y + _lastImageStretch.Height / 2);
            Vector2 scale = new Vector2((float)_lastImageStretch.Width / (float)_renderTarget.Width, (float)_lastImageStretch.Height / (float)_renderTarget.Height);
            spriteBatch.Begin();
            spriteBatch.Draw(_renderTarget, position, null,  Color.White, _rotationAngle, origin, scale, SpriteEffects.None, 0);
            spriteBatch.End();

            _btnRotateBoard.Draw();
        }

        public void OnLButtonDown(Point point)
        {
            if (_captureControl != null)
            {
                _captureControl.OnLButtonDown(point);
            }
            else
            {
                // convert mouse to texture coordinate
                Point texturePoint = ScreenCoorToTextureCoor(point);
                if (texturePoint != Point.Zero)
                {
                    if (_mainBoard.InEditMode == false)
                    {
                        UpdateDragAndDrop(texturePoint, enumButtonEvent.LeftPressed);
                   }
                }

                foreach (var item in childControls)
                    item.OnLButtonDown(point);
            }
        }

        public void OnLButtonUp(Point point)
        {
            if (_captureControl != null)
            {
                _captureControl.OnLButtonUp(point);
            }
            else
            {
                // convert mouse to texture coordinate
                Point texturePoint = ScreenCoorToTextureCoor(point);
                if (texturePoint != Point.Zero)
                {
                    if (this.InEditMode == true)
                    {
                        UpdateEditMode(texturePoint, true);
                    }
                    if (_mainBoard.InEditMode == false)
                    {
                        UpdateDragAndDrop(texturePoint, enumButtonEvent.LeftReleased);
                    }

                }

                foreach (var item in childControls)
                    item.OnLButtonUp(point);
            }
        }

        public void OnMouseMoving(Point point)
        {
            if (_captureControl != null)
            {
                _captureControl.OnMouseMoving(point);
            }
            else
            {
                // convert mouse to texture coordinate
                Point texturePoint = ScreenCoorToTextureCoor(point);
                if (texturePoint != Point.Zero)
                {
                    if (_mainBoard.InEditMode == false)
                    {
                        UpdateHighlightFields(texturePoint);
                        UpdateDragAndDrop(texturePoint, enumButtonEvent.Nothing);
                    }
                }

                foreach (var item in childControls)
                    item.OnMouseMoving(point);
            }
        }

        public void OnRButtonDown(Point point)
        {
            if (_captureControl != null)
            {
                _captureControl.OnRButtonDown(point);
            }
            else
            {
                // convert mouse to texture coordinate
                Point texturePoint = ScreenCoorToTextureCoor(point);
                if (texturePoint != Point.Zero)
                {
                }

                foreach (var item in childControls)
                    item.OnRButtonDown(point);
            }
        }

        public void OnRButtonUp(Point point)
        {
            if (_captureControl != null)
            {
                _captureControl.OnRButtonUp(point);
            }
            else
            {
                // convert mouse to texture coordinate
                Point texturePoint = ScreenCoorToTextureCoor(point);
                if (texturePoint != Point.Zero)
                {
                    if (this.InEditMode == true)
                    {
                        UpdateEditMode(texturePoint, false);
                    }
                }

                foreach (var item in childControls)
                    item.OnRButtonUp(point);
            }
        }


        void _btnRotateBoard_ButtonPressed(object sender, EventArgs e)
        {
            RotateBoard();
        }

        protected void RotateBoard()
        {
            _rotationAnimation = new RotationBoardAnimation(0, (float)Math.PI, 0, 0, TimeSpan.FromMilliseconds(1000));
            _rotationAnimation.StartAnimation();
        }


        protected void RenderFigures()
        {
            for(int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                {
                    enumFigureType figureType = m_GameManager.GameBoard.GetField(row, col);
                    if (figureType != enumFigureType.Nothing)
                    {
                        // if animated, dont draw
                        Point dummy;
                        if (_animationParams.GetPoint(new Position(row, col), out dummy) == true)
                            continue;
                        // if dragged, dont draw
                        if (row == _posDragFigure.Row && col == _posDragFigure.Col)
                            continue;

                        Texture2D figureTexture = GetFigureTexture(figureType);
                        Rectangle fieldRect = Rectangle.Empty;
                        GetFieldRect(new Position(row, col), out fieldRect);
                        float alpha = _animationParams.GetAlphaColor(new Position(row, col));

                        spriteBatch.Draw(figureTexture, fieldRect, Color.White * alpha);
                    }
                } // for
        }

        protected void RenderAnimatedFigures()
        {
            foreach (var item in _animationParams.FigurePoints)
            {
                enumFigureType figureType = m_GameManager.GameBoard.GetField(item.Key);
                Texture2D figureTexture = GetFigureTexture(figureType);
                Rectangle fieldRect = Rectangle.Empty;
                GetFieldRect(item.Key, out fieldRect);
                Rectangle animRect = new Rectangle(item.Value.X, item.Value.Y, fieldRect.Width, fieldRect.Height);

                spriteBatch.Draw(figureTexture, animRect, Color.White);
            }
        }

        protected void RenderHighlightFields()
        {
            foreach (var position in _listHighlightFields)
            {
                Rectangle rect = Rectangle.Empty;
                GetFieldRect(position, out rect);
                float size = (float)rect.Width / 2f;
                rect = new Rectangle((int)(rect.X + size / 2), (int)(rect.Y + size / 2), (int)size, (int)size);
                spriteBatch.Draw(_txtHighlightField, rect, Color.White);
            }
        }

        protected void RenderDraggedFigure()
        {
            if (_posDragFigure != Position.Empty)
            {
                enumFigureType figureType = m_GameManager.GameBoard.GetField(_posDragFigure);
                Texture2D figureTexture = GetFigureTexture(figureType);
                Rectangle rect = new Rectangle(_pointDragPoint.X, _pointDragPoint.Y, figureTexture.Width, figureTexture.Height);

                spriteBatch.Draw(figureTexture, rect, Color.White);
            }
        }

        protected void RenderDropField()
        {
            if (_posDropField != Position.Empty)
            {
                Rectangle rect = GetFieldRect(_posDropField);
                spriteBatch.Draw(_txtDropFieldTexture, rect, Color.White);
            }

        }

        protected void UpdateHighlightFields(Point point)
        {
            _listHighlightFields.Clear();

            Position pos;
            GetFieldByPoint(point, out pos);
            if (pos.Row == -1 || pos.Col == -1)
                return;

            if (m_GameManager.GameBoard.GetField(pos) == enumFigureType.Nothing)
                return;
            if (m_GameManager.GameBoard.GetField(pos).FigureColor() != m_GameManager.GameBoard.OnMove)
                return;

            m_GameManager.GetFigureMoves(pos, _listHighlightFields);       
        }

        protected void UpdateEditMode(Point point, bool leftButton)
        {
            Position pos = Position.Empty;
            GetFieldByPoint(point, out pos);
            if (pos.Row != -1 && pos.Col != -1)
            {
                if (leftButton == true)
                {
                    _mainBoard.ClickOnField(pos, true);
                }
                else
                {
                    _mainBoard.ClickOnField(pos, false);
                }
            }
        }



        protected void UpdateDragAndDrop(Point point, enumButtonEvent buttonEvent)
        {
            if (_posDragFigure == Position.Empty)
            {
                UpdateStartDrag(point, buttonEvent);
            }
            else
            {
                UpdateDragging(point);
                UpdateDropping(point, buttonEvent);
            }
        }

        protected void UpdateStartDrag(Point point, enumButtonEvent buttonEvent)
        {
            if (buttonEvent == enumButtonEvent.LeftPressed)
            {
                Position pos;
                GetFieldByPoint(point, out pos);

                if (pos.Row == -1 || pos.Col == -1)
                    return;

                if (m_GameManager.GameBoard.GetField(pos) == enumFigureType.Nothing)
                    return;
                if (m_GameManager.GameBoard.GetField(pos).FigureColor() != m_GameManager.GameBoard.OnMove)
                    return;

                List<Position> dummy = new List<Position>(); ;
                m_GameManager.GetFigureMoves(pos, dummy);
                if (dummy.Count > 0)
                {
                    _posDragFigure = pos;
                    _mainBoard.SetCapture(this);
                }
            }

        }

        protected void UpdateDragging(Point point)
        {
            enumFigureType figureType = m_GameManager.GameBoard.GetField(_posDragFigure);
            if (figureType == enumFigureType.Nothing)
                return;

            Texture2D txt = GetFigureTexture(figureType);
            int x = point.X - txt.Width / 2;
            int y = point.Y - txt.Height / 2;
            
            if (x < 0)
                x = 0;
            if (y < 0)
                y = 0;
            if (x + txt.Width > _txtMainBoard.Width)
                x = _txtMainBoard.Width - txt.Width;
            if (y + txt.Height > _txtMainBoard.Height)
                y = _txtMainBoard.Height - txt.Height;

            _pointDragPoint = new Point(x, y);

            _posDropField = Position.Empty;
            Position pos;
            GetFieldByPoint(point, out pos);
            if (pos.Row != -1 && pos.Col != -1)
            {
                if (m_GameManager.CanFigureMoveHere(_posDragFigure, pos) == true)
                {
                    _posDropField = pos;
                }
            }
        }

        protected void UpdateDropping(Point point, enumButtonEvent buttonEvent)
        {
            if (buttonEvent == enumButtonEvent.LeftReleased)
            {
                Position startPos = _posDragFigure;
                Position endPos = _posDropField;

                _pointDragPoint = Point.Zero;
                _posDragFigure = Position.Empty;
                _posDropField = Position.Empty;
                _mainBoard.ReleaseCapture();

                if (startPos != Position.Empty && endPos != Position.Empty)
                {
                    if (m_GameManager.CanFigureMoveHere(startPos, endPos) == true)
                    {
                        Move move = m_GameManager.CompleteMoveStep(startPos, endPos);
                        m_GameManager.DoMoveStep(move);
                        AddMove(move);
                    }
                }
            }
        }

        public void SetData(GameManager _gameManager)
        {
            m_GameManager = _gameManager;
        }

        #region Business Implementation

        protected Texture2D GetFigureTexture(enumFigureType figureType)
        {
            switch(figureType)
            {
                case enumFigureType.WhiteMan:
                    return _txtWhiteMan;
                case enumFigureType.BlackMan:
                    return _txtBlackMan;
                case enumFigureType.WhiteKing:
                    return _txtWhiteKing;
                case enumFigureType.BlackKing:
                    return _txtBlackKing;
            } // switch

            return null;
        }

        #endregion

        #region Animations

        internal AnimationBlock ComposeAnimation(Move completeMove)
        {
            AnimationBlock animBlock = new AnimationBlock();
            Move move = completeMove;
            Position figurePos = move.startPos;
            while (move != null)
            {
                int animLength = 500 * Math.Abs(move.endPos.Row - move.startPos.Row);

                animBlock.AddAnimation(new Slide2DMoveAnimation(figurePos,
                    TimeSpan.FromMilliseconds(animLength),
                    GetFieldRect(move.startPos).Location,
                    GetFieldRect(move.endPos).Location));


                if (move.RemovingFigure == true)
                {
                    animBlock.AddAnimation(new JumpedFigureAnimation(move.removedFigurePlace,
                            TimeSpan.FromMilliseconds(500)));
                }

                move = move.nextMove;
            }

            return animBlock;
        }

        public void AnimateMove(Move move)
        {
            AnimationBlock animBlock = ComposeAnimation(move);

            animBlock.AnimationEvent += (figurePos, position, alpha, point) =>
            {
                if (point == Point.Zero)
                    _animationParams.SetAlphaColor(figurePos, alpha);
                else
                    _animationParams.SetPoint(figurePos, point);
            };


            animBlock.StartAnimation();

            while (animBlock.UpdateAnimation())
            {
                _game.Tick();
            }

            _animationParams.Clear();
        }

        #endregion

        #region Helper board metrics methods

        // rotates coordinates of the board if rotated
        protected void CheckCoordinatesRotated(ref Position pos)
        {
            if (this.RotatedBlackDown == true)
            {
                pos.Row = 7 - pos.Row;
                pos.Col = 7 - pos.Col;
            }
        }

        protected Rectangle GetFieldRect(Position pos)
        {
            Rectangle rect = Rectangle.Empty;
            GetFieldRect(pos, out rect);
            return rect;
        }

        protected void GetFieldRect(Position pos, out Rectangle fieldRect)
        {
            CheckCoordinatesRotated(ref pos);

            // calculate the field
            float fieldX = pos.Col * m_fieldSize + m_startX;
            float fieldY = (m_numFields - pos.Row - 1) * m_fieldSize + m_startY;
            fieldRect = new Rectangle((int)fieldX, (int)fieldY, (int)m_fieldSize, (int)m_fieldSize);
        }

        protected void GetFieldByPoint(Point point, out Position pos)
        {
            pos = Position.Empty;

            double x = point.X - m_startX;
            double y = point.Y - m_startY;
            if (x < 0 || y < 0)
                return;

            x = x / m_fieldSize;
            y = y / m_fieldSize;

            int col = (int)Math.Floor(x);
            int row = (int)m_numFields - (int)Math.Floor(y) - 1;

            if (row >= m_numFields || col >= m_numFields)
                return;

            pos = new Position(row, col);
            CheckCoordinatesRotated(ref pos);
        }

        protected Point ScreenCoorToTextureCoor(Point point)
        {
            if (point.X < _lastImageStretch.X || point.Y < _lastImageStretch.Y)
                return Point.Zero;
            if (point.X > _lastImageStretch.Right || point.Y > _lastImageStretch.Bottom)
                return Point.Zero;

            int x = point.X - _lastImageStretch.X;
            int y = point.Y - _lastImageStretch.Y;
            x = (int)((float)x * (float)_txtMainBoard.Width / (float)_lastImageStretch.Width);
            y = (int)((float)y * (float)_txtMainBoard.Height / (float)_lastImageStretch.Height);

            return new Point(x, y);
        }

        protected void CalculateImageStretch()
        {
            int widthWindow = this.GameBoardWidth;
            int heightWindow = this.GameBoardHeight;

            int imageWidth = widthWindow;
            int imageHeight = heightWindow - _imageTopMargin;
            
            int minSize = Math.Min(imageWidth, imageHeight);

            int posX = (widthWindow - minSize) / 2;
            int posY = _imageTopMargin + (heightWindow - minSize - _imageTopMargin) / 2;

            _lastImageStretch = new Rectangle(posX, posY, minSize, minSize);
        }

        #endregion



        public void SetCapture(Common.IBoardControl control)
        {
            _captureControl = control;
            _mainBoard.SetCapture(this);
        }

        public void ReleaseCapture()
        {
            _captureControl = null;
            _mainBoard.ReleaseCapture();
            
        }

        public void AddMove(Move move)
        {
            if (_currentMove == null)
            {
                _currentMove = move;
            }
            else
            {
                Move lastMove = _currentMove;
                while (lastMove.nextMove != null)
                    lastMove = lastMove.nextMove;
                lastMove.nextMove = move;
            }

            if (m_GameManager.DoesMoveContinue(move, move.endPos) == false)
            {
                CallMoveDoneEvent();
            }
        }

        protected void CallMoveDoneEvent()
        {
            GameMoveDoneEventArgs e = new GameMoveDoneEventArgs();
            e.move = _currentMove;
            _currentMove = null;

            if (GameMoveDoneEvent != null)
            {
                GameMoveDoneEvent(this, e);
            }
        }

        public void ClickOnField(Position pos, bool left)
        {
            throw new NotImplementedException();
        }
    }
}
